{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "3b0afef0",
   "metadata": {},
   "source": [
    "NN_PNEUMONIA | Original file is located at\n",
    "https://github.com/wwwmyroot/nn_pneumonia/NN_pneumonia_MAIN.py\n",
    "\n",
    "LATER: Добавить выгрузку весов."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "af1d6064",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ---- TRY: решение дисконнекта GoogleCollab\n",
    "# ---- Не понадобилось, но сохраню на всякий случай ----\n",
    "# -- v0\n",
    "# from IPython.display import Audio\n",
    "#   # import numpy as np\n",
    "#\n",
    "# Audio(np.array([0] * 2 * 3600 * 3000, dtype=np.int8), normalize=False, rate=3000, autoplay=True)\n",
    "#   # Audio([None] * 2 * 3600 * 3000, normalize=False, rate=3000, autoplay=True)\n",
    "#\n",
    "# -- v1: F-12 and input in dev-tools console\n",
    "#\n",
    "# function ClickConnect(){\n",
    "#  console.log(\"Working\");\n",
    "#  document.querySelector(\"colab-toolbar-button#connect\").click()\n",
    "#}\n",
    "#setInterval(ClickConnect, 60000)\n",
    "#\n",
    "# -- v2: python solution\n",
    "# import numpy as np\n",
    "# import time\n",
    "# import mouse\n",
    "# while True:\n",
    "    # random_row = np.random.random_sample()*100\n",
    "    # random_col = np.random.random_sample()*10\n",
    "    # random_time = np.random.random_sample()*np.random.random_sample() * 100\n",
    "    # mouse.wheel(1000)\n",
    "    # mouse.wheel(-1000)\n",
    "    # mouse.move(random_row, random_col, absolute=False, duration=0.2)\n",
    "    # mouse.move(-random_row, -random_col, absolute=False, duration = 0.2)\n",
    "    # mouse.LEFT\n",
    "    # time.sleep(random_time)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cdcd6518",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title <h1><b>---- INIT_STEP ---- Подключение библиотек.</h1></b>\n",
    "# ---- INIT_STEP ---- Подключение библиотек.\n",
    "# -- Библиотека TensorFlow\n",
    "import tensorflow as tf\n",
    "#\n",
    "# -- Библиотека Numpy\n",
    "import numpy as np\n",
    "#\n",
    "# -- Библиотека keras\n",
    "import keras\n",
    "#\n",
    "# -- Подключение нужных слоев из модуля tensorflow.keras.layers\n",
    "from tensorflow.keras.models import Sequential\n",
    "from tensorflow.keras.layers import Conv2D, MaxPooling2D, Dense, Flatten, BatchNormalization, Dropout\n",
    "from tensorflow.keras.optimizers import Adam\n",
    "#\n",
    "# -- Генератор аугментированных изображений\n",
    "from tensorflow.keras.preprocessing.image import ImageDataGenerator\n",
    "#\n",
    "# -- Матрица ошибок классификатора\n",
    "from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay\n",
    "#\n",
    "# -- Для отрисовки изображений\n",
    "from PIL import Image\n",
    "#\n",
    "# -- Для разделения на обучающую и проверочную/тестовую выборку\n",
    "from sklearn.model_selection import train_test_split\n",
    "# from keras.utils.vis_utils import plot_model\n",
    "from keras.utils import plot_model\n",
    "#\n",
    "# -- Для отрисовки графиков\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "sns.set_style('darkgrid')\n",
    "%matplotlib inline\n",
    "#\n",
    "# -- Для работы с файлами\n",
    "import os\n",
    "#\n",
    "# -- Для работы с архивами в питоне\n",
    "import zipfile\n",
    "# -- Для генерации случайных чисел\n",
    "import random\n",
    "#\n",
    "# -- Модуль для для загрузки данных из облака\n",
    "import gdown\n",
    "import gc\n",
    "#\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "78fae549",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title <h1><b>---- WORKER_2 ---- </h1></b>\n",
    "# ---- WORKER_2 ----\n",
    "#\n",
    "class Worker2:\n",
    "  def __init__(self):\n",
    "    self.IMAGE_PATH = '/content/data/'\n",
    "    self.CLASS_LIST = None\n",
    "    self.CLASS_COUNT = None\n",
    "#\n",
    "  def load_dataset(self):\n",
    "    # Загрузка из облака\n",
    "    gdown.download('https://storage.yandexcloud.net/aiueducation/marketing/datasets/chest_xray.zip', None)\n",
    "    # Разархивация датасета в директорию '/content/data'\n",
    "    # -- в коллабе:\n",
    "    !unzip -qo \"chest_xray.zip\" -d /content/data\n",
    "    #\n",
    "    # -- CLI python:\n",
    "    # python -m zipfile -e chest_xray.zip /content/data/\n",
    "    #\n",
    "    # средствами питона\n",
    "    if not os.path.exists(\"/content/data\"):\n",
    "      os.makedirs(\"/content/data\")\n",
    "    #\n",
    "    try:\n",
    "      with zipfile.ZipFile(\"chest_xray.zip\", \"r\") as zip_ref:\n",
    "        zip_ref.extractall(\"/content/data\")\n",
    "    except zipfile.BadZipFile:\n",
    "      print(\"(!)ERROR: Файл архива поврежден или не является архивом.\")\n",
    "#\n",
    "#\n",
    "    self.CLASS_LIST = sorted(os.listdir(self.IMAGE_PATH))\n",
    "    self.CLASS_COUNT = len(self.CLASS_LIST)\n",
    "    print(f'Датасет загружен!\\n')\n",
    "    print(f'Количество классов: {self.CLASS_COUNT}')\n",
    "    res = \"\\n  * \".join(self.CLASS_LIST)\n",
    "    print(f'Метки классов:\\n  * {res}')\n",
    "#\n",
    "  def show_samples(self, count = 3):\n",
    "    n_rows = count\n",
    "    # Создание заготовки для изображений всех классов\n",
    "    fig, axs = plt.subplots(n_rows, self.CLASS_COUNT, figsize=(35, 5*n_rows-5))\n",
    "    # Для всех номеров классов:\n",
    "    for i in range(self.CLASS_COUNT):\n",
    "      for j in range(n_rows):\n",
    "        # Формирование пути к папке содержимого класса\n",
    "        car_path = f'{self.IMAGE_PATH}{self.CLASS_LIST[i]}/'\n",
    "        # Выбор случайного фото из i-го класса\n",
    "        img_path = car_path + random.choice(os.listdir(car_path))\n",
    "        # Отображение фотографии\n",
    "        axs[j][i].set_title(self.CLASS_LIST[i])\n",
    "        axs[j][i].imshow(Image.open(img_path))\n",
    "        axs[j][i].axis('off')\n",
    "    # Отрисовка всего полотна\n",
    "    plt.show()\n",
    "#\n",
    "  def dataset_info(self):\n",
    "    # Cписок путей к файлам картинок\n",
    "    self.data_files = []\n",
    "    # Список меток классов, соответствующих файлам\n",
    "    self.data_labels = []\n",
    "    # Количество файлов каждого класса\n",
    "    cnt_files = []\n",
    "    # Для всех классов по порядку номеров (их меток)\n",
    "    for class_label in range(self.CLASS_COUNT):\n",
    "        # Выборка имени класса из списка имен\n",
    "        class_name = self.CLASS_LIST[class_label]\n",
    "        # Формирование полного пути к папке с изображениями класса\n",
    "        class_path = self.IMAGE_PATH + class_name\n",
    "        # Получение списка имен файлов с изображениями текущего класса\n",
    "        class_files = os.listdir(class_path)\n",
    "        cnt_files.append(len(class_files))\n",
    "        print(f'Размер класса {class_name} составляет {len(class_files)}')\n",
    "        # Добавление к общему списку всех файлов класса с добавлением родительского пути\n",
    "        self.data_files += [f'{class_path}/{file_name}' for file_name in class_files]\n",
    "        # Добавление к общему списку меток текущего класса - их ровно столько, сколько файлов в классе\n",
    "        self.data_labels += [class_label] * len(class_files)\n",
    "#\n",
    "    print('\\nОбщий размер базы для обучения:', len(self.data_labels))\n",
    "    plt.figure(figsize=(25, 8))\n",
    "    plt.bar(self.CLASS_LIST, cnt_files)\n",
    "    plt.show()\n",
    "#\n",
    "  def create_sets(self, size=(64, 64)):\n",
    "    # Ширина изображения\n",
    "    IMG_WIDTH = size[0]\n",
    "    # Высота изображения\n",
    "    IMG_HEIGHT = size[1]\n",
    "    # Пустой список для данных изображений\n",
    "    data_images = []\n",
    "#\n",
    "    for file_name in self.data_files:\n",
    "        # Открытие и смена размера изображения\n",
    "        img = Image.open(file_name).resize((IMG_WIDTH, IMG_HEIGHT))\n",
    "        # Конвертация изображений в режиме CMYK в режим RGB\n",
    "        if img.mode == 'CMYK':\n",
    "          img = img.convert('RGB')\n",
    "#\n",
    "        # Перевод в numpy-массив\n",
    "        img_np = np.array(img)\n",
    "        # Добавление изображения в виде numpy-массива к общему списку\n",
    "        data_images.append(img_np)\n",
    "#\n",
    "#\n",
    "    # Перевод общего списка изображений в numpy-массив\n",
    "    x_data = np.array(data_images)\n",
    "    # Перевод общего списка меток класса в numpy-массив\n",
    "    y_data = np.array(self.data_labels)\n",
    "#\n",
    "    # Нормированние массива изображений\n",
    "    x_data = x_data / 255.\n",
    "                                                    # набор параметров\n",
    "    x_train, x_test, y_train, y_test = train_test_split(x_data,\n",
    "                                                    # набор меток классов\n",
    "                                                    y_data,\n",
    "                                                    # процент в тестовую\n",
    "                                                    test_size=0.2,\n",
    "                                                    #  перемешивание\n",
    "                                                    shuffle=True,\n",
    "                                                    # воспроизводимость\n",
    "                                                    random_state=3)\n",
    "    print(f'Размер обучающей выборки: {x_train.shape}')\n",
    "    print(f'Размер проверочной выборки: {x_test.shape}')\n",
    "    self.sets = (x_train, x_test, y_train, y_test)\n",
    "    gc.collect()\n",
    "    return x_train, x_test, y_train, y_test\n",
    "#\n",
    "  def create_sets_augmentation(self, size=(64, 64)):\n",
    "    # Папка для обучающего набора данных\n",
    "    TRAIN_PATH          = '/content/data'\n",
    "    # Доля тестовых данных в общем наборе\n",
    "    TEST_SPLIT          = 0.1\n",
    "    # Доля проверочной выборки в обучающем наборе\n",
    "    VAL_SPLIT           = 0.2\n",
    "    # Ширина изображения для нейросети\n",
    "    IMG_WIDTH           = size[0]\n",
    "    # Высота изображения для нейросети\n",
    "    IMG_HEIGHT          = size[1]\n",
    "    # Количество каналов (для RGB равно 3, для Grey равно 1)\n",
    "    IMG_CHANNELS        = 3\n",
    "    # Параметры аугментации\n",
    "    ## Пределы поворота\n",
    "    ROTATION_RANGE      = 8\n",
    "    ## Пределы сдвига по горизонтали\n",
    "    WIDTH_SHIFT_RANGE   = 0.15\n",
    "    ## Пределы сдвига по вертикали\n",
    "    HEIGHT_SHIFT_RANGE  = 0.15\n",
    "    ## Пределы увеличения/уменьшения\n",
    "    ZOOM_RANGE          = 0.15\n",
    "    ## Пределы изменения яркости\n",
    "    BRIGHTNESS_RANGE    = (0.7, 1.3)\n",
    "    ## Горизонтальное отражение разрешено\n",
    "    HORIZONTAL_FLIP     = True\n",
    "#\n",
    "    # Число эпох обучения\n",
    "    EPOCHS              = 60\n",
    "    # Размер батча для обучения модели\n",
    "    BATCH_SIZE          = 24\n",
    "    # Оптимизатор\n",
    "    OPTIMIZER           = Adam(0.0001)\n",
    "    train_datagen = ImageDataGenerator(\n",
    "                    rescale=1. / 255.,\n",
    "                    rotation_range=ROTATION_RANGE,\n",
    "                    width_shift_range=WIDTH_SHIFT_RANGE,\n",
    "                    height_shift_range=HEIGHT_SHIFT_RANGE,\n",
    "                    zoom_range=ZOOM_RANGE,\n",
    "                    brightness_range=BRIGHTNESS_RANGE,\n",
    "                    horizontal_flip=HORIZONTAL_FLIP,\n",
    "                    validation_split=VAL_SPLIT\n",
    "                )\n",
    "    self.train_generator = train_datagen.flow_from_directory(\n",
    "              # Путь к обучающим изображениям\n",
    "              TRAIN_PATH,\n",
    "              # Параметры требуемого размера изображения\n",
    "              target_size=size,\n",
    "              # Размер батча\n",
    "              batch_size=BATCH_SIZE,\n",
    "              # Тип набора данных\n",
    "              class_mode='categorical',\n",
    "              # Перемешивание\n",
    "              shuffle=True,\n",
    "              # Указание сгенерировать обучающую выборку\n",
    "              subset='training'\n",
    "          )\n",
    "#\n",
    "    # Проверочная выборка также генерируется из папки обучающего набора\n",
    "    self.validation_generator = train_datagen.flow_from_directory(\n",
    "        TRAIN_PATH,\n",
    "        target_size=size,\n",
    "        batch_size=BATCH_SIZE,\n",
    "        class_mode='categorical',\n",
    "        shuffle=True,\n",
    "        # Указание сгенерировать проверочную выборку\n",
    "        subset='validation'\n",
    "    )\n",
    "    print('Выборки сформированы')\n",
    "#\n",
    "#\n",
    "#\n",
    "  def ShowConfusionMatrix(self, model, x_test, y_test):\n",
    "    cm = confusion_matrix(y_test,\n",
    "                          np.argmax(model.predict(x_test, verbose=0), axis=1),\n",
    "                          normalize = 'true')\n",
    "#\n",
    "    # Отрисовка матрицы ошибок\n",
    "    fig, ax = plt.subplots(figsize=(10, 10))\n",
    "    ax.set_title(f'матрица ошибок', fontsize=18)\n",
    "    disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=self.CLASS_LIST)\n",
    "    disp.plot(ax=ax)\n",
    "    # Стирание ненужной цветовой шкалы\n",
    "    plt.gca().images[-1].colorbar.remove()\n",
    "    plt.xlabel('Предсказанные классы', fontsize=16)\n",
    "    plt.ylabel('Верные классы', fontsize=16)\n",
    "    plt.grid(False)\n",
    "    # Наклон меток горизонтальной оси при необходимости\n",
    "    fig.autofmt_xdate(rotation=45)\n",
    "    plt.show()\n",
    "#\n",
    "  def show_learning_information(self, history, model, augmentation=False):\n",
    "    print('График обучения:')\n",
    "    # Создание полотна для рисунка\n",
    "    plt.figure(1, figsize=(10, 6))\n",
    "#\n",
    "    # Отрисовка графиков 'accuracy' и 'val_accuracy' из значений словаря store_learning.history\n",
    "    plt.plot(history.history['accuracy'],\n",
    "            label='Доля верных ответов на обучающем наборе')\n",
    "    plt.plot(history.history['val_accuracy'],\n",
    "            label='Доля верных ответов на проверочном наборе')\n",
    "    # Задание подписей осей\n",
    "    plt.xlabel('Эпоха обучения')\n",
    "    plt.ylabel('Доля верных ответов')\n",
    "    plt.legend()\n",
    "    # Фиксация графиков и рисование всей картинки\n",
    "    plt.show()\n",
    "    print(f'Максимальная точность на обучающей выборке: {round(np.array(history.history[\"accuracy\"]).max(), 3)}')\n",
    "    print(f'Максимальная точность на проверочной выборке: {round(np.array(history.history[\"val_accuracy\"]).max(), 3)}')\n",
    "#\n",
    "    if not augmentation:\n",
    "      print('\\nМатрица ошибок для обучающей выборки:')\n",
    "      self.ShowConfusionMatrix(model, self.sets[0], self.sets[2])\n",
    "      print('\\nМатрица ошибок для проверочной выборки:')\n",
    "      self.ShowConfusionMatrix(model, self.sets[1], self.sets[3])\n",
    "#\n",
    "  def train_model(self, funcModel, count):\n",
    "    # Обучение модели сверточной нейронной сети на подготовленных данных\n",
    "    cnt = count\n",
    "    history_accuracy = []\n",
    "    history_val_accuracy = []\n",
    "    for i in range(cnt):\n",
    "      model = funcModel()\n",
    "      store_learning = model.fit(x_train,\n",
    "                                y_train,\n",
    "                                validation_data=(x_test, y_test),\n",
    "                                shuffle=True,\n",
    "                                batch_size=25,\n",
    "                                epochs=50,\n",
    "                                verbose=0)\n",
    "      history_accuracy.append(store_learning.history['accuracy'])\n",
    "      history_val_accuracy.append(store_learning.history['val_accuracy'])\n",
    "      print(f'Обучение №{i+1} завершено. Результаты')\n",
    "      print(f'Максимальная точность на обучающей выборке: {round(np.array(store_learning.history[\"accuracy\"]).max(), 3)}')\n",
    "      print(f'Максимальная точность на проверочной выборке: {round(np.array(store_learning.history[\"val_accuracy\"]).max(), 3)}')\n",
    "      plt.figure(1, figsize=(10, 6))\n",
    "      plt.plot(store_learning.history['accuracy'],\n",
    "              label='Доля верных ответов на обучающем наборе')\n",
    "      plt.plot(store_learning.history['val_accuracy'],\n",
    "              label='Доля верных ответов на проверочном наборе')\n",
    "      plt.xlabel('Эпоха обучения')\n",
    "      plt.ylabel('Доля верных ответов')\n",
    "      plt.legend()\n",
    "      plt.show()\n",
    "      print('\\n'*3)\n",
    "    print(f'Средняя точность на обучающей выборке: {round(np.array(history_accuracy).max(axis=1).mean(), 3)}')\n",
    "    print(f'Максимальная точность на обучающей выборке: {round(np.array(history_accuracy).max(), 3)}')\n",
    "    print(f'Средняя точность на проверочной выборке: {round(np.array(history_val_accuracy).max(axis=1).mean(), 3)}')\n",
    "    print(f'Максимальная точность на проверочной выборке: {round(np.array(history_val_accuracy).max(), 3)}')\n",
    "#\n",
    "  def train_model_augmentation(self, funcModel, count):\n",
    "    # Обучение модели сверточной нейронной сети на подготовленных данных\n",
    "    cnt = count\n",
    "    history_accuracy = []\n",
    "    history_val_accuracy = []\n",
    "    for i in range(cnt):\n",
    "      model = funcModel()\n",
    "      store_learning = model.fit(self.train_generator,\n",
    "                           validation_data=self.validation_generator,\n",
    "                           batch_size=25,\n",
    "                           epochs=20,\n",
    "                           verbose=1)\n",
    "      history_accuracy.append(store_learning.history['accuracy'])\n",
    "      history_val_accuracy.append(store_learning.history['val_accuracy'])\n",
    "      print(f'Обучение №{i+1} завершено. Результаты')\n",
    "      print(f'Максимальная точность на обучающей выборке: {round(np.array(store_learning.history[\"accuracy\"]).max(), 3)}')\n",
    "      print(f'Максимальная точность на проверочной выборке: {round(np.array(store_learning.history[\"val_accuracy\"]).max(), 3)}')\n",
    "      plt.figure(1, figsize=(10, 6))\n",
    "      plt.plot(store_learning.history['accuracy'],\n",
    "              label='Доля верных ответов на обучающем наборе')\n",
    "      plt.plot(store_learning.history['val_accuracy'],\n",
    "              label='Доля верных ответов на проверочном наборе')\n",
    "      plt.xlabel('Эпоха обучения')\n",
    "      plt.ylabel('Доля верных ответов')\n",
    "      plt.legend()\n",
    "      plt.show()\n",
    "      print('\\n'*3)\n",
    "    print(f'Средняя точность на обучающей выборке: {round(np.array(history_accuracy).max(axis=1).mean(), 3)}')\n",
    "    print(f'Максимальная точность на обучающей выборке: {round(np.array(history_accuracy).max(), 3)}')\n",
    "    print(f'Средняя точность на проверочной выборке: {round(np.array(history_val_accuracy).max(axis=1).mean(), 3)}')\n",
    "    print(f'Максимальная точность на проверочной выборке: {round(np.array(history_val_accuracy).max(), 3)}')\n",
    "#\n",
    "  def getdataforpretrainmodel(self, image_size):\n",
    "    # Присваиваем путь к базе в переменную data_dir\n",
    "    data_dir = '/content/data/'\n",
    "    # Определяем batch size\n",
    "    batch_size = 32\n",
    "    train_ds = tf.keras.preprocessing.image_dataset_from_directory(\n",
    "      # Путь к изображениям\n",
    "      data_dir,\n",
    "      # Устанавливаем разделение на обучающую и проверочную выборки (20%)\n",
    "      validation_split = 0.2,\n",
    "      # Помечаем, что выборка обучающая (80%)\n",
    "      subset = \"training\",\n",
    "      # Дополнительное случайное начальное число для перетасовки и преобразований\n",
    "      seed = 123,\n",
    "      # Размер изображений, который был задан ранее\n",
    "      image_size = image_size,\n",
    "      # Размер батча, который был задан ранее\n",
    "      batch_size = batch_size)\n",
    "#\n",
    "    val_ds = tf.keras.preprocessing.image_dataset_from_directory(\n",
    "      # Путь к изображениям\n",
    "      data_dir,\n",
    "      # Устанавливаем разделение на обучающую и проверочную выборки (20%)\n",
    "      validation_split = 0.2,\n",
    "      # Помечаем, что выборка проверочная (20%)\n",
    "      subset = \"validation\",\n",
    "      # Дополнительное случайное начальное число для перетасовки и преобразований\n",
    "      seed = 123,\n",
    "      # Размер изображений, который был задан ранее\n",
    "      image_size = image_size,\n",
    "      # Размер батча, который был задан ранее\n",
    "      batch_size = batch_size)\n",
    "#\n",
    "    # Определяем имена классов:\n",
    "    class_names = train_ds.class_names\n",
    "    AUTOTUNE = tf.data.AUTOTUNE\n",
    "    # Предварительная обучающая выборка\n",
    "    train_ds = train_ds.cache().prefetch(buffer_size=AUTOTUNE)\n",
    "    # Предварительная тестовая выборка\n",
    "    val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)\n",
    "#\n",
    "    # Дополнительная аугментация в виде слоя для модели (для увеличения базы изображений)\n",
    "    data_augmentation = tf.keras.Sequential([\n",
    "      # Произвольное увеличение и уменьшение на 10%\n",
    "      tf.keras.layers.experimental.preprocessing.RandomZoom((-0.05, 0.05)),\n",
    "      # Изменение контраста изображений\n",
    "      tf.keras.layers.experimental.preprocessing.RandomContrast(0.15)])\n",
    "    return class_names, data_augmentation, train_ds, val_ds\n",
    "#\n",
    "  @staticmethod\n",
    "  def plot_graph(train_acc, val_acc, train_loss, val_loss):\n",
    "    '''\n",
    "    Функция выведет на экран графики точности и ошибки на проверочной и обучающей выборках.\n",
    "    '''\n",
    "    sns.set(style='darkgrid', palette='dark')\n",
    "    plt.figure(figsize=(16, 10))\n",
    "    plt.subplot(2, 2, 1)\n",
    "    plt.title('Точность', fontweight='bold')\n",
    "    plt.plot(train_acc, label='Точность на обучащей выборке')\n",
    "    plt.plot(val_acc, label='Точность на проверочной выборке')\n",
    "    plt.xlabel('Эпоха обучения')\n",
    "    plt.ylabel('Доля верных ответов')\n",
    "    plt.legend()\n",
    "#\n",
    "    plt.subplot(2, 2, 2)\n",
    "    plt.title('Ошибка', fontweight='bold')\n",
    "    plt.plot(train_loss, label='Ошибка на обучающей выборке')\n",
    "    plt.plot(val_loss, label='Ошибка на проверочной выборке')\n",
    "    plt.xlabel('Эпоха обучения')\n",
    "    plt.ylabel('Ошибка')\n",
    "    plt.legend()\n",
    "#\n",
    "    plt.show()\n",
    "#\n",
    "  def createAndTrainNASNetMobile(self):\n",
    "    # Определяем размер изображений\n",
    "    image_size = (224, 224)\n",
    "    data = self.getdataforpretrainmodel(image_size)\n",
    "#\n",
    "#\n",
    "    # Нормализация данных на вход в соответствии требованиям модели\n",
    "    preprocess_input = tf.keras.applications.nasnet.preprocess_input\n",
    "    # Сборка мусора для освобождения памяти\n",
    "    gc.collect()\n",
    "    # Определяем входную размерность в соответствии требованиям модели\n",
    "    image_shape = image_size + (3,)\n",
    "    # Задаем слой GlobalAveragePooling2D\n",
    "    global_average_layer = tf.keras.layers.GlobalAveragePooling2D()\n",
    "    # Задаем выходной слой на нужное нам количество классов\n",
    "    prediction_layer = tf.keras.layers.Dense(len(data[0]))\n",
    "#\n",
    "    # Создаем базовую модель NASNetMobile:\n",
    "    base_model = tf.keras.applications.nasnet.NASNetMobile(input_shape=image_shape,\n",
    "                            # Не включаем верхнюю часть модели, чтобы можно было использовать свои входные размерности\n",
    "                            include_top=False,\n",
    "                            # Используем веса уже предобученной модели на базе Imagenet\n",
    "                            weights='imagenet')\n",
    "#\n",
    "#\n",
    "    # Собираем полную модель, состоящую из всех наших определенных слоев и базовой модели NASNetMobile:\n",
    "    inputs = tf.keras.Input(shape=(image_shape))\n",
    "    x = data[1](inputs)\n",
    "    x = preprocess_input(x)\n",
    "    x = base_model(x, training=False)\n",
    "    x = global_average_layer(x)\n",
    "    x = tf.keras.layers.Dropout(0.2)(x)\n",
    "    outputs = prediction_layer(x)\n",
    "    model = tf.keras.Model(inputs, outputs)\n",
    "    # Задаем значение шага обучения\n",
    "    base_learning_rate = 0.0001\n",
    "    # Задаем количество эпох\n",
    "    epochs = 20\n",
    "#\n",
    "    # Компилируем модель:\n",
    "    ## Оптимизатор Adam c заданным ранее шагом обучения\n",
    "    model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=base_learning_rate),\n",
    "                  # Ошибка SparseCategoricalCrossentropy\n",
    "                  loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
    "                  # Метрика Accuracy\n",
    "                  metrics=['accuracy'])\n",
    "    # Засекаем время для измерения времени обучения нейронной сети\n",
    "    CurrentTime = time.time()\n",
    "#\n",
    "    # Обучаем модель:\n",
    "    # Обучающая выборка\n",
    "    hNASNetMobile = model.fit(data[2],\n",
    "                              # Количество эпох обучения, заданное ранее\n",
    "                              epochs=epochs,\n",
    "                              # Проверочная выборка\n",
    "                              validation_data=data[3])\n",
    "#\n",
    "    # После того, как пройдет обучение, выведем графики точности и ошибки:\n",
    "    self.plot_graph(hNASNetMobile.history['accuracy'],\n",
    "              hNASNetMobile.history['val_accuracy'],\n",
    "              hNASNetMobile.history['loss'],\n",
    "              hNASNetMobile.history['val_loss'])\n",
    "#\n",
    "    print('Время обучения: {:.0f} секунд ({:.2f} минут)'.format(time.time() - CurrentTime, (time.time() - CurrentTime)/60))\n",
    "    print(f'Максимальная точность на обучающей выборке: {round(max(hNASNetMobile.history[\"accuracy\"]),3)}')\n",
    "    print(f'Максимальная точность на проверочной выборке: {round(max(hNASNetMobile.history[\"val_accuracy\"]),3)}')\n",
    "#\n",
    "    return model\n",
    "#\n",
    "  def createAndTrainResNet(self):\n",
    "    # Определяем размер изображений\n",
    "    image_size = (224, 224)\n",
    "    data = self.getdataforpretrainmodel(image_size)\n",
    "#\n",
    "#\n",
    "    # Нормализация данных на вход в соответствии требованиям модели\n",
    "    preprocess_input = tf.keras.applications.resnet50.preprocess_input\n",
    "    # Сборка мусора для освобождения памяти\n",
    "    gc.collect()\n",
    "    # Определяем входную размерность в соответствии требованиям модели\n",
    "    image_shape = image_size + (3,)\n",
    "    # Задаем слой GlobalAveragePooling2D\n",
    "    global_average_layer = tf.keras.layers.GlobalAveragePooling2D()\n",
    "    # Задаем выходной слой на нужное нам количество классов\n",
    "    prediction_layer = tf.keras.layers.Dense(len(data[0]))\n",
    "#\n",
    "    # Создаем базовую модель ResNet50:\n",
    "    base_model = tf.keras.applications.resnet50.ResNet50(input_shape=image_shape,\n",
    "                            # Не включаем верхнюю часть модели, чтобы можно было использовать свои входные размерности\n",
    "                            include_top=False,\n",
    "                            # Используем веса уже предобученной модели на базе Imagenet\n",
    "                            weights='imagenet')\n",
    "    # Собираем полную модель, состоящую из всех наших определенных слоев и базовой модели ResNet50:\n",
    "    inputs = tf.keras.Input(shape=(image_shape))\n",
    "    x = data[1](inputs)\n",
    "    x = preprocess_input(x)\n",
    "    x = base_model(x, training=False)\n",
    "    x = global_average_layer(x)\n",
    "    x = tf.keras.layers.Dropout(0.2)(x)\n",
    "    outputs = prediction_layer(x)\n",
    "    model = tf.keras.Model(inputs, outputs)\n",
    "    # Задаем значение шага обучения\n",
    "    base_learning_rate = 0.0001\n",
    "    # Задаем количество эпох\n",
    "    epochs = 20\n",
    "#\n",
    "    # Компилируем модель:\n",
    "    ## Оптимизатор Adam c заданным ранее шагом обучения\n",
    "    model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=base_learning_rate),\n",
    "                  # Ошибка SparseCategoricalCrossentropy\n",
    "                  loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
    "                  # Метрика Accuracy\n",
    "                  metrics=['accuracy'])\n",
    "#\n",
    "    # Засекаем время для измерения времени обучения нейронной сети\n",
    "    CurrentTime = time.time()\n",
    "#\n",
    "    # Обучаем модель:\n",
    "    ## Обучающая выборка\n",
    "    hResNet50 = model.fit(data[2],\n",
    "                          # Количество эпох обучения, заданное ранее\n",
    "                          epochs=epochs,\n",
    "                          # Проверочная выборка\n",
    "                          validation_data=data[3])\n",
    "#\n",
    "    # После того, как пройдет обучение, выведем графики точности и ошибки:\n",
    "    self.plot_graph(hResNet50.history['accuracy'],\n",
    "              hResNet50.history['val_accuracy'],\n",
    "              hResNet50.history['loss'],\n",
    "              hResNet50.history['val_loss'])\n",
    "#\n",
    "    print('Время обучения: {:.0f} секунд ({:.2f} минут)'.format(time.time() - CurrentTime, (time.time() - CurrentTime)/60))\n",
    "    print(f'Максимальная точность на обучающей выборке: {round(max(hResNet50.history[\"accuracy\"]),3)}')\n",
    "    print(f'Максимальная точность на проверочной выборке: {round(max(hResNet50.history[\"val_accuracy\"]),3)}')\n",
    "#\n",
    "    return model\n",
    "#\n",
    "  def createAndTrainResNet50V2(self):\n",
    "    # Определяем размер изображений\n",
    "    image_size = (224, 224)\n",
    "    data = self.getdataforpretrainmodel(image_size)\n",
    "#\n",
    "    # Нормализация данных на вход в соответствии требованиям модели\n",
    "    preprocess_input = tf.keras.applications.resnet_v2.preprocess_input\n",
    "    # Сборка мусора для освобождения памяти\n",
    "    gc.collect()\n",
    "    # Определяем входную размерность в соответствии требованиям модели\n",
    "    image_shape = image_size + (3,)\n",
    "    # Задаем слой GlobalAveragePooling2D\n",
    "    global_average_layer = tf.keras.layers.GlobalAveragePooling2D()\n",
    "    # Задаем выходной слой на нужное нам количество классов\n",
    "    prediction_layer = tf.keras.layers.Dense(len(data[0]))\n",
    "#\n",
    "#\n",
    "    # Создаем базовую модель ResNet50V2:\n",
    "    base_model = tf.keras.applications.resnet_v2.ResNet50V2(input_shape=image_shape,\n",
    "                            # Не включаем верхнюю часть модели, чтобы можно было использовать свои входные размерности\n",
    "                            include_top=False,\n",
    "                            # Используем веса уже предобученной модели на базе Imagenet\n",
    "                            weights='imagenet')\n",
    "#\n",
    "#\n",
    "    # Собираем полную модель, состоящую из всех наших определенных слоев и базовой модели ResNet50V2:\n",
    "    inputs = tf.keras.Input(shape=(image_shape))\n",
    "    x = data[1](inputs)\n",
    "    x = preprocess_input(x)\n",
    "    x = base_model(x, training=False)\n",
    "    x = global_average_layer(x)\n",
    "    x = tf.keras.layers.Dropout(0.2)(x)\n",
    "    outputs = prediction_layer(x)\n",
    "    model = tf.keras.Model(inputs, outputs)\n",
    "#\n",
    "    # Задаем значение шага обучения\n",
    "    base_learning_rate = 0.0001\n",
    "    # Задаем количество эпох\n",
    "    epochs = 20\n",
    "#\n",
    "    # Компилируем модель:\n",
    "    ## Оптимизатор Adam c заданным ранее шагом обучения\n",
    "    model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=base_learning_rate),\n",
    "                # Ошибка SparseCategoricalCrossentropy\n",
    "                loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
    "                # Метрика Accuracy\n",
    "                metrics=['accuracy'])\n",
    "#\n",
    "    # Засекаем время для измерения времени обучения нейронной сети\n",
    "    CurrentTime = time.time()\n",
    "#\n",
    "    # Обучаем модель:\n",
    "    ## Обучающая выборка\n",
    "    hResNet50V2 = model.fit(data[2],\n",
    "                            # Количество эпох обучения, заданное ранее\n",
    "                            epochs=epochs,\n",
    "                            # Проверочная выборка\n",
    "                            validation_data=data[3])\n",
    "#\n",
    "    # После того, как пройдет обучение, выведем графики точности и ошибки:\n",
    "    self.plot_graph(hResNet50V2.history['accuracy'],\n",
    "            hResNet50V2.history['val_accuracy'],\n",
    "            hResNet50V2.history['loss'],\n",
    "            hResNet50V2.history['val_loss'])\n",
    "#\n",
    "    print('Время обучения: {:.0f} секунд ({:.2f} минут)'.format(time.time() - CurrentTime, (time.time() - CurrentTime)/60))\n",
    "    print(f'Максимальная точность на обучающей выборке: {round(max(hResNet50V2.history[\"accuracy\"]),3)}')\n",
    "    print(f'Максимальная точность на проверочной выборке: {round(max(hResNet50V2.history[\"val_accuracy\"]),3)}')\n",
    "#\n",
    "    return model\n",
    "#\n",
    "#\n",
    "  def createAndTrainVGG16(self):\n",
    "    # Определяем размер изображений\n",
    "    image_size = (224, 224)\n",
    "    data = self.getdataforpretrainmodel(image_size)\n",
    "#\n",
    "    # Нормализация данных на вход в соответствии требованиям модели\n",
    "    preprocess_input = tf.keras.applications.vgg16.preprocess_input\n",
    "    # Сборка мусора для освобождения памяти\n",
    "    gc.collect()\n",
    "    # Определяем входную размерность в соответствии требованиям модели\n",
    "    image_shape = image_size + (3,)\n",
    "    # Задаем слой GlobalAveragePooling2D\n",
    "    global_average_layer = tf.keras.layers.GlobalAveragePooling2D()\n",
    "    # Задаем выходной слой на нужное нам количество классов\n",
    "    prediction_layer = tf.keras.layers.Dense(len(data[0]))\n",
    "#\n",
    "#\n",
    "# Создаем базовую модель VGG16:\n",
    "    base_model = tf.keras.applications.vgg16.VGG16(input_shape=image_shape,\n",
    "                            # Не включаем верхнюю часть модели, чтобы можно было использовать свои входные размерности\n",
    "                            include_top=False,\n",
    "                            # Используем веса уже предобученной модели на базе Imagenet\n",
    "                            weights='imagenet')\n",
    "#\n",
    "#\n",
    "    # Собираем полную модель, состоящую из всех наших определенных слоев и базовой модели VGG16:\n",
    "    inputs = tf.keras.Input(shape=(image_shape))\n",
    "    x = data[1](inputs)\n",
    "    x = preprocess_input(x)\n",
    "    x = base_model(x, training=False)\n",
    "    x = global_average_layer(x)\n",
    "    x = tf.keras.layers.Dropout(0.2)(x)\n",
    "    outputs = prediction_layer(x)\n",
    "    model = tf.keras.Model(inputs, outputs)\n",
    "    # Задаем значение шага обучения\n",
    "    base_learning_rate = 0.0001\n",
    "    # Задаем количество эпох\n",
    "    epochs = 20\n",
    "#\n",
    "    # Компилируем модель:\n",
    "    ## Оптимизатор Adam c заданным ранее шагом обучения\n",
    "    model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=base_learning_rate),\n",
    "                  # Ошибка SparseCategoricalCrossentropy\n",
    "                  loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
    "                  # Метрика Accuracy\n",
    "                  metrics=['accuracy'])\n",
    "#\n",
    "    # Засекаем время для измерения времени обучения нейронной сети\n",
    "    CurrentTime = time.time()\n",
    "#\n",
    "    # Обучаем модель:\n",
    "    ## Обучающая выборка\n",
    "    hVGG16 = model.fit(data[2],\n",
    "                      # Количество эпох обучения, заданное ранее\n",
    "                      epochs=epochs,\n",
    "                      # Проверочная выборка\n",
    "                      validation_data=data[3])\n",
    "#\n",
    "    # После того, как пройдет обучение, выведем графики точности и ошибки:\n",
    "    self.plot_graph(hVGG16.history['accuracy'],\n",
    "              hVGG16.history['val_accuracy'],\n",
    "              hVGG16.history['loss'],\n",
    "              hVGG16.history['val_loss'])\n",
    "#\n",
    "    print('Время обучения: {:.0f} секунд ({:.2f} минут)'.format(time.time() - CurrentTime, (time.time() - CurrentTime)/60))\n",
    "    print(f'Максимальная точность на обучающей выборке: {round(max(hVGG16.history[\"accuracy\"]),3)}')\n",
    "    print(f'Максимальная точность на проверочной выборке: {round(max(hVGG16.history[\"val_accuracy\"]),3)}')\n",
    "#\n",
    "    return model\n",
    "#\n",
    "#\n",
    "  def createAndTrainVGG19(self):\n",
    "    # Определяем размер изображений\n",
    "    image_size = (224, 224)\n",
    "    data = self.getdataforpretrainmodel(image_size)\n",
    "#\n",
    "    # Нормализация данных на вход в соответствии требованиям модели\n",
    "    preprocess_input = tf.keras.applications.vgg19.preprocess_input\n",
    "    # Сборка мусора для освобождения памяти\n",
    "    gc.collect()\n",
    "    # Определяем входную размерность в соответствии требованиям модели\n",
    "    image_shape = image_size + (3,)\n",
    "    # Задаем слой GlobalAveragePooling2D\n",
    "    global_average_layer = tf.keras.layers.GlobalAveragePooling2D()\n",
    "    # Задаем выходной слой на нужное нам количество классов\n",
    "    prediction_layer = tf.keras.layers.Dense(len(data[0]))\n",
    "#\n",
    "#\n",
    "# Создаем базовую модель VGG 19:\n",
    "    base_model = tf.keras.applications.vgg19.VGG19(input_shape=image_shape,\n",
    "                            # Не включаем верхнюю часть модели, чтобы можно было использовать свои входные размерности\n",
    "                            include_top=False,\n",
    "                            # Используем веса уже предобученной модели на базе Imagenet\n",
    "                            weights='imagenet')\n",
    "#\n",
    "#\n",
    "# Собираем полную модель, состоящую из всех наших определенных слоев и базовой модели VGG 19:\n",
    "    inputs = tf.keras.Input(shape=(image_shape))\n",
    "    x = data[1](inputs)\n",
    "    x = preprocess_input(x)\n",
    "    x = base_model(x, training=False)\n",
    "    x = global_average_layer(x)\n",
    "    x = tf.keras.layers.Dropout(0.2)(x)\n",
    "    outputs = prediction_layer(x)\n",
    "    model = tf.keras.Model(inputs, outputs)\n",
    "    # Задаем значение шага обучения\n",
    "    base_learning_rate = 0.0001\n",
    "    # Задаем количество эпох\n",
    "    epochs = 20\n",
    "#\n",
    "# Компилируем модель:\n",
    "## Оптимизатор Adam c заданным ранее шагом обучения\n",
    "    model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=base_learning_rate),\n",
    "                  # Ошибка SparseCategoricalCrossentropy\n",
    "                  loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
    "                  # Метрика Accuracy\n",
    "                  metrics=['accuracy'])\n",
    "#\n",
    "    # Засекаем время для измерения времени обучения нейронной сети\n",
    "    CurrentTime = time.time()\n",
    "#\n",
    "    # Обучаем модель:\n",
    "    ## Обучающая выборка\n",
    "    hVGG19 = model.fit(data[2],\n",
    "                      # Количество эпох обучения, заданное ранее\n",
    "                      epochs=epochs,\n",
    "                      # Проверочная выборка\n",
    "                      validation_data=data[3])\n",
    "#\n",
    "    # После того, как пройдет обучение, выведем графики точности и ошибки:\n",
    "    self.plot_graph(hVGG19.history['accuracy'],\n",
    "              hVGG19.history['val_accuracy'],\n",
    "              hVGG19.history['loss'],\n",
    "              hVGG19.history['val_loss'])\n",
    "#\n",
    "    print('Время обучения: {:.0f} секунд ({:.2f} минут)'.format(time.time() - CurrentTime, (time.time() - CurrentTime)/60))\n",
    "    print(f'Максимальная точность на обучающей выборке: {round(max(hVGG19.history[\"accuracy\"]),3)}')\n",
    "    print(f'Максимальная точность на проверочной выборке: {round(max(hVGG19.history[\"val_accuracy\"]),3)}')\n",
    "#\n",
    "    return model\n",
    "#\n",
    "#\n",
    "  def createAndTrainXception(self):\n",
    "   # Определяем размер изображений\n",
    "   image_size = (299, 299)\n",
    "   data = self.getdataforpretrainmodel(image_size)\n",
    "#\n",
    "   # Сборка мусора для освобождения памяти\n",
    "   gc.collect()\n",
    "   # Нормализация данных на вход в соответствии требованиям модели\n",
    "   preprocess_input = tf.keras.applications.xception.preprocess_input\n",
    "   # Определяем входную размерность в соответствии требованиям модели\n",
    "   image_shape = image_size + (3,)\n",
    "   # Задаем слой GlobalAveragePooling2D\n",
    "   global_average_layer = tf.keras.layers.GlobalAveragePooling2D()\n",
    "   # Задаем выходной слой на нужное нам количество классов\n",
    "   prediction_layer = tf.keras.layers.Dense(len(data[0]))\n",
    "#\n",
    "#\n",
    "   # Создаем базовую модель Xception:\n",
    "   base_model = tf.keras.applications.xception.Xception(input_shape=image_shape,\n",
    "                            # Не включаем верхнюю часть модели, чтобы можно было использовать свои входные размерности\n",
    "                            include_top=False,\n",
    "                            # Используем веса уже предобученной модели на базе Imagenet\n",
    "                            weights='imagenet')\n",
    "#\n",
    "#\n",
    "    # Собираем полную модель, состоящую из всех наших определенных слоев и базовой модели Xception:\n",
    "   inputs = tf.keras.Input(shape=(image_shape))\n",
    "   x = data[1](inputs)\n",
    "   x = preprocess_input(x)\n",
    "   x = base_model(x, training=False)\n",
    "   x = global_average_layer(x)\n",
    "   x = tf.keras.layers.Dropout(0.2)(x)\n",
    "   outputs = prediction_layer(x)\n",
    "   model = tf.keras.Model(inputs, outputs)\n",
    "#\n",
    "   # Задаем значение шага обучения\n",
    "   base_learning_rate = 0.0001\n",
    "   # Задаем количество эпох\n",
    "   epochs = 20\n",
    "#\n",
    "   # Компилируем модель:\n",
    "   ## Оптимизатор Adam c заданным ранее шагом обучения\n",
    "   model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=base_learning_rate),\n",
    "                 # Ошибка SparseCategoricalCrossentropy\n",
    "                 loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
    "                 # Метрика Accuracy\n",
    "                 metrics=['accuracy'])\n",
    "    # Засекаем время для измерения времени обучения нейронной сети\n",
    "   CurrentTime = time.time()\n",
    "#\n",
    "    # Обучаем модель:\n",
    "    ## Обучающая выборка\n",
    "   hXception = model.fit(data[2],\n",
    "                         # Количество эпох обучения, заданное ранее\n",
    "                         epochs=epochs,\n",
    "                         # Проверочная выборка\n",
    "                         validation_data=data[3])\n",
    "#\n",
    "   # После того, как пройдет обучение, выведем графики точности и ошибки:\n",
    "   self.plot_graph(hXception.history['accuracy'],\n",
    "             hXception.history['val_accuracy'],\n",
    "             hXception.history['loss'],\n",
    "             hXception.history['val_loss'])\n",
    "#\n",
    "   print('Время обучения: {:.0f} секунд ({:.2f} минут)'.format(time.time() - CurrentTime, (time.time() - CurrentTime)/60))\n",
    "   print(f'Максимальная точность на обучающей выборке: {round(max(hXception.history[\"accuracy\"]),3)}')\n",
    "   print(f'Максимальная точность на проверочной выборке: {round(max(hXception.history[\"val_accuracy\"]),3)}')\n",
    "   return model\n",
    "#\n",
    "  def createAndTrainEfficientNet(self):\n",
    "    # Определяем размер изображений\n",
    "    image_size = (299, 299)\n",
    "    data = self.getdataforpretrainmodel(image_size)\n",
    "#\n",
    "    # Сборка мусора для освобождения памяти\n",
    "    gc.collect()\n",
    "    # Нормализация данных на вход в соответствии требованиям модели\n",
    "    preprocess_input = tf.keras.applications.efficientnet.preprocess_input\n",
    "    # Определяем входную размерность в соответствии требованиям модели\n",
    "    image_shape = image_size + (3,)\n",
    "    # Задаем слой GlobalAveragePooling2D\n",
    "    global_average_layer = tf.keras.layers.GlobalAveragePooling2D()\n",
    "    # Задаем выходной слой на нужное нам количество классов\n",
    "    prediction_layer = tf.keras.layers.Dense(len(data[0]))\n",
    "#\n",
    "#\n",
    "    # Создаем базовую модель EfficientNet:\n",
    "    base_model = tf.keras.applications.efficientnet.EfficientNetB7(input_shape=image_shape,\n",
    "                            # Не включаем верхнюю часть модели, чтобы можно было использовать свои входные размерности\n",
    "                            include_top=False,\n",
    "                            # Используем веса уже предобученной модели на базе Imagenet\n",
    "                            weights='imagenet')\n",
    "#\n",
    "#\n",
    "    #  Собираем полную модель, состоящую из всех наших определенных слоев и базовой модели EfficientNet:\n",
    "    inputs = tf.keras.Input(shape=(image_shape))\n",
    "    x = data[1](inputs)\n",
    "    x = preprocess_input(x)\n",
    "    x = base_model(x, training=False)\n",
    "    x = global_average_layer(x)\n",
    "    x = tf.keras.layers.Dropout(0.2)(x)\n",
    "    outputs = prediction_layer(x)\n",
    "    model = tf.keras.Model(inputs, outputs)\n",
    "    # Задаем значение шага обучения\n",
    "    base_learning_rate = 0.0001\n",
    "    # Задаем количество эпох\n",
    "    epochs = 20\n",
    "#\n",
    "    # Компилируем модель:\n",
    "    # Оптимизатор Adam c заданным ранее шагом обучения\n",
    "    model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=base_learning_rate),\n",
    "                  # Ошибка SparseCategoricalCrossentropy\n",
    "                  loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
    "                  # Метрика Accuracy\n",
    "                  metrics=['accuracy'])\n",
    "#\n",
    "    # Засекаем время для измерения времени обучения нейронной сети\n",
    "    CurrentTime = time.time()\n",
    "#\n",
    "# Обучаем модель:\n",
    "    # Обучающая выборка\n",
    "    hEfficientNet = model.fit(data[2],\n",
    "                              # Количество эпох обучения, заданное ранее\n",
    "                              epochs=epochs,\n",
    "                              # Проверочная выборка\n",
    "                              validation_data=data[3])\n",
    "#\n",
    "    # После того, как пройдет обучение, выведем графики точности и ошибки:\n",
    "    self.plot_graph(hEfficientNet.history['accuracy'],\n",
    "              hEfficientNet.history['val_accuracy'],\n",
    "              hEfficientNet.history['loss'],\n",
    "              hEfficientNet.history['val_loss'])\n",
    "#\n",
    "    print('Время обучения: {:.0f} секунд ({:.2f} минут)'.format(time.time() - CurrentTime, (time.time() - CurrentTime)/60))\n",
    "    print(f'Максимальная точность на обучающей выборке: {round(max(hEfficientNet.history[\"accuracy\"]),3)}')\n",
    "    print(f'Максимальная точность на проверочной выборке: {round(max(hEfficientNet.history[\"val_accuracy\"]),3)}')\n",
    "#\n",
    "    return model\n",
    "#\n",
    "  def createAndTrainNASNetLarge(self):\n",
    "    # Определяем размер изображений\n",
    "    image_size = (331, 331)\n",
    "    data = self.getdataforpretrainmodel(image_size)\n",
    "#\n",
    "    # Нормализация данных на вход в соответствии требованиям модели\n",
    "    preprocess_input = tf.keras.applications.nasnet.preprocess_input\n",
    "    # Определяем входную размерность в соответствии требованиям модели\n",
    "    image_shape = image_size + (3,)\n",
    "    # Задаем слой GlobalAveragePooling2D\n",
    "    global_average_layer = tf.keras.layers.GlobalAveragePooling2D()\n",
    "    # Задаем выходной слой на нужное нам количество классов\n",
    "    prediction_layer = tf.keras.layers.Dense(len(data[0]))\n",
    "#\n",
    "#\n",
    "    # Создаем базовую модель NASNetLarge:\n",
    "    base_model = tf.keras.applications.nasnet.NASNetLarge(input_shape=image_shape,\n",
    "                            # Не включаем верхнюю часть модели, чтобы можно было использовать свои входные размерности\n",
    "                            include_top=False,\n",
    "                            # Используем веса уже предобученной модели на базе Imagenet\n",
    "                            weights='imagenet')\n",
    "#\n",
    "#\n",
    "    # Собираем полную модель, состоящую из всех наших определенных слоев и базовой модели NASNetLarge:\n",
    "    inputs = tf.keras.Input(shape=(image_shape))\n",
    "    x = data[1](inputs)\n",
    "    x = base_model(x, training=False)\n",
    "    x = global_average_layer(x)\n",
    "    x = tf.keras.layers.Dropout(0.2)(x)\n",
    "    outputs = prediction_layer(x)\n",
    "    model = tf.keras.Model(inputs, outputs)\n",
    "    # Задаем значение шага обучения\n",
    "    base_learning_rate = 0.0001\n",
    "    # Задаем количество эпох\n",
    "    epochs = 20\n",
    "#\n",
    "# Компилируем модель:\n",
    "    # Оптимизатор Adam c заданным ранее шагом обучения\n",
    "    model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=base_learning_rate),\n",
    "                  # Ошибка SparseCategoricalCrossentropy\n",
    "                  loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
    "                  # Метрика Accuracy\n",
    "                  metrics=['accuracy'])\n",
    "#\n",
    "    # Засекаем время для измерения времени обучения нейронной сети\n",
    "    CurrentTime = time.time()\n",
    "#\n",
    "    # Обучаем модель:\n",
    "    ## Обучающая выборка\n",
    "    hNASNetLarge = model.fit(data[2],\n",
    "                          # Количество эпох обучения, заданное ранее\n",
    "                          epochs=epochs,\n",
    "                          # Проверочная выборка\n",
    "                          validation_data=data[3])\n",
    "#\n",
    "    # После того, как пройдет обучение, выведем графики точности и ошибки:\n",
    "    self.plot_graph(hNASNetLarge.history['accuracy'],\n",
    "              hNASNetLarge.history['val_accuracy'],\n",
    "              hNASNetLarge.history['loss'],\n",
    "              hNASNetLarge.history['val_loss'])\n",
    "#\n",
    "    print('Время обучения: {:.0f} секунд ({:.2f} минут)'.format(time.time() - CurrentTime, (time.time() - CurrentTime)/60))\n",
    "    print(f'Максимальная точность на обучающей выборке: {round(max(hNASNetLarge.history[\"accuracy\"]),3)}')\n",
    "    print(f'Максимальная точность на проверочной выборке: {round(max(hNASNetLarge.history[\"val_accuracy\"]),3)}')\n",
    "#\n",
    "    return model\n",
    "#\n",
    "worker_pretrain=Worker2()\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "41efb949",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title <h1><b>---- WORKER ----</h1></b>\n",
    "#\n",
    "class Worker:\n",
    "  def __init__(self):\n",
    "    self.IMAGE_PATH = '/content/data/'\n",
    "    self.CLASS_LIST = None\n",
    "    self.CLASS_COUNT = None\n",
    "#\n",
    "  def load_dataset(self):\n",
    "    gdown.download('https://storage.yandexcloud.net/aiueducation/marketing/datasets/chest_xray.zip', None)\n",
    "    # Разархивация датасета в директорию '/content/data'\n",
    "    # -- в коллабе:\n",
    "    !unzip -qo \"chest_xray.zip\" -d /content/data\n",
    "    #\n",
    "    # -- CLI python:\n",
    "    # python -m zipfile -e chest_xray.zip /content/data/\n",
    "    #\n",
    "    # средствами питона\n",
    "    if not os.path.exists(\"/content/data\"):\n",
    "      os.makedirs(\"/content/data\")\n",
    "    #\n",
    "    try:\n",
    "      with zipfile.ZipFile(\"chest_xray.zip\", \"r\") as zip_ref:\n",
    "        zip_ref.extractall(\"/content/data\")\n",
    "    except zipfile.BadZipFile:\n",
    "      print(\"(!)ERROR: Файл архива поврежден или не является архивом.\")\n",
    "    #\n",
    "    #\n",
    "    self.CLASS_LIST = sorted(os.listdir(self.IMAGE_PATH))\n",
    "    self.CLASS_COUNT = len(self.CLASS_LIST)\n",
    "    print(f'Датасет загружен!\\n')\n",
    "    print(f'Количество классов: {self.CLASS_COUNT}')\n",
    "    res = \"\\n  * \".join(self.CLASS_LIST)\n",
    "    print(f'Метки классов:\\n  * {res}')\n",
    "#\n",
    "  def show_samples(self, count = 3):\n",
    "   n_rows = count\n",
    "   # Создание заготовки для изображений всех классов\n",
    "   fig, axs = plt.subplots(n_rows, self.CLASS_COUNT, figsize=(35, 5*n_rows-5))\n",
    "#\n",
    "    # Для всех номеров классов:\n",
    "   for i in range(self.CLASS_COUNT):\n",
    "     for j in range(n_rows):\n",
    "       # Формирование пути к папке содержимого класса\n",
    "       car_path = f'{self.IMAGE_PATH}{self.CLASS_LIST[i]}/'\n",
    "       # Выбор случайного фото из i-го класса\n",
    "       img_path = car_path + random.choice(os.listdir(car_path))\n",
    "       # Отображение фотографии (подробнее будет объяснено далее)\n",
    "       axs[j][i].set_title(self.CLASS_LIST[i])\n",
    "       axs[j][i].imshow(Image.open(img_path), cmap='gray')\n",
    "       axs[j][i].axis('off')\n",
    "    # Отрисовка всего полотна\n",
    "   plt.show()\n",
    "#\n",
    "  def dataset_info(self):\n",
    "    # Cписок путей к файлам картинок\n",
    "    self.data_files = []\n",
    "    # Список меток классов, соответствующих файлам\n",
    "    self.data_labels = []\n",
    "    # Количество файлов каждого класса\n",
    "    cnt_files = []\n",
    "#\n",
    "    # Для всех классов по порядку номеров (их меток)\n",
    "    for class_label in range(self.CLASS_COUNT):\n",
    "        # Выборка имени класса из списка имен\n",
    "        class_name = self.CLASS_LIST[class_label]\n",
    "        # Формирование полного пути к папке с изображениями класса\n",
    "        class_path = self.IMAGE_PATH + class_name\n",
    "        # Получение списка имен файлов с изображениями текущего класса\n",
    "        class_files = os.listdir(class_path)\n",
    "        cnt_files.append(len(class_files))\n",
    "        print(f'Размер класса {class_name} составляет {len(class_files)}')\n",
    "        # Добавление к общему списку всех файлов класса с добавлением родительского пути\n",
    "        self.data_files += [f'{class_path}/{file_name}' for file_name in class_files]\n",
    "#\n",
    "        # Добавление к общему списку меток текущего класса - их ровно столько, сколько файлов в классе\n",
    "        self.data_labels += [class_label] * len(class_files)\n",
    "#\n",
    "    print('\\nОбщий размер базы для обучения:', len(self.data_labels))\n",
    "    plt.figure(figsize=(25, 8))\n",
    "    plt.bar(self.CLASS_LIST, cnt_files)\n",
    "    plt.show()\n",
    "#\n",
    "  def create_sets(self, size=(64, 64)):\n",
    "    # Ширина изображения\n",
    "    IMG_WIDTH = size[0]\n",
    "    # Высота изображения\n",
    "    IMG_HEIGHT = size[1]\n",
    "    # Пустой список для данных изображений\n",
    "    data_images = []\n",
    "#\n",
    "    for file_name in self.data_files:\n",
    "        # Открытие и смена размера изображения\n",
    "        img = Image.open(file_name).resize((IMG_WIDTH, IMG_HEIGHT)).convert('L')\n",
    "#\n",
    "        # Конвертация изображений в режиме CMYK в режим RGB\n",
    "        if img.mode == 'CMYK':\n",
    "          img = img.convert('RGB')\n",
    "#\n",
    "        # Перевод в numpy-массив\n",
    "        img_np = np.array(img)\n",
    "        # Добавление изображения в виде numpy-массива к общему списку\n",
    "        data_images.append(img_np)\n",
    "#\n",
    "#\n",
    "    # Перевод общего списка изображений в numpy-массив\n",
    "    x_data = np.array(data_images)\n",
    "    # Перевод общего списка меток класса в numpy-массив\n",
    "    y_data = np.array(self.data_labels)\n",
    "#\n",
    "    # Нормированние массива изображений\n",
    "    x_data = x_data / 255.\n",
    "    # набор параметров\n",
    "    x_train, x_test, y_train, y_test = train_test_split(x_data,\n",
    "                                                    # набор меток классов\n",
    "                                                    y_data,\n",
    "                                                    # процент в тестовую\n",
    "                                                    test_size=0.2,\n",
    "                                                    #  перемешивание\n",
    "                                                    shuffle=True,\n",
    "                                                    # воспроизводимость\n",
    "                                                    random_state=3)\n",
    "    print(f'Размер обучающей выборки: {x_train.shape}')\n",
    "    print(f'Размер проверочной выборки: {x_test.shape}')\n",
    "    self.sets = (x_train, x_test, y_train, y_test)\n",
    "    gc.collect()\n",
    "    return x_train, x_test, y_train, y_test\n",
    "#\n",
    "  def create_sets_augmentation(self, size=(64, 64)):\n",
    "    # Папка для обучающего набора данных\n",
    "    TRAIN_PATH          = '/content/data'\n",
    "    # Доля тестовых данных в общем наборе\n",
    "    TEST_SPLIT          = 0.1\n",
    "    # Доля проверочной выборки в обучающем наборе\n",
    "    VAL_SPLIT           = 0.2\n",
    "    # Ширина изображения для нейросети\n",
    "    IMG_WIDTH           = size[0]\n",
    "    # Высота изображения для нейросети\n",
    "    IMG_HEIGHT          = size[1]\n",
    "    # Количество каналов (для RGB равно 3, для Grey равно 1)\n",
    "    IMG_CHANNELS        = 3\n",
    "    # -- Параметры аугментации\n",
    "    # Пределы поворота\n",
    "    ROTATION_RANGE      = 8\n",
    "    # Пределы сдвига по горизонтали\n",
    "    WIDTH_SHIFT_RANGE   = 0.15\n",
    "    # Пределы сдвига по вертикали\n",
    "    HEIGHT_SHIFT_RANGE  = 0.15\n",
    "    # Пределы увеличения/уменьшения\n",
    "    ZOOM_RANGE          = 0.15\n",
    "    # Пределы изменения яркости\n",
    "    BRIGHTNESS_RANGE    = (0.7, 1.3)\n",
    "    # Горизонтальное отражение разрешено\n",
    "    HORIZONTAL_FLIP     = True\n",
    "#\n",
    "    # Число эпох обучения\n",
    "    EPOCHS              = 60\n",
    "    # Размер батча для обучения модели\n",
    "    BATCH_SIZE          = 24\n",
    "    # Оптимизатор\n",
    "    OPTIMIZER           = Adam(0.0001)\n",
    "    train_datagen = ImageDataGenerator(\n",
    "                    rescale=1. / 255.,\n",
    "                    rotation_range=ROTATION_RANGE,\n",
    "                    width_shift_range=WIDTH_SHIFT_RANGE,\n",
    "                    height_shift_range=HEIGHT_SHIFT_RANGE,\n",
    "                    zoom_range=ZOOM_RANGE,\n",
    "                    brightness_range=BRIGHTNESS_RANGE,\n",
    "                    horizontal_flip=HORIZONTAL_FLIP,\n",
    "                    validation_split=VAL_SPLIT\n",
    "                )\n",
    "    self.train_generator = train_datagen.flow_from_directory(\n",
    "              # Путь к обучающим изображениям\n",
    "              TRAIN_PATH,\n",
    "              # Параметры требуемого размера изображения\n",
    "              target_size=size,\n",
    "              # Размер батча\n",
    "              batch_size=BATCH_SIZE,\n",
    "              # Тип набора данных\n",
    "              class_mode='categorical',\n",
    "              # Перемешивание\n",
    "              shuffle=True,\n",
    "              # Указание сгенерировать обучающую выборку\n",
    "              subset='training'\n",
    "          )\n",
    "#\n",
    "    # Проверочная выборка также генерируется из папки обучающего набора\n",
    "    self.validation_generator = train_datagen.flow_from_directory(\n",
    "        TRAIN_PATH,\n",
    "        target_size=size,\n",
    "        batch_size=BATCH_SIZE,\n",
    "        class_mode='categorical',\n",
    "        shuffle=True,\n",
    "        # Указание сгенерировать проверочную выборку\n",
    "        subset='validation'\n",
    "    )\n",
    "    print('Выборки сформированы')\n",
    "#\n",
    "#\n",
    "  def ShowConfusionMatrix(self, model, x_test, y_test):\n",
    "    pred = model.predict(x_test, verbose=0)\n",
    "    y_pred = [1 * (x[0]>=0.5) for x in pred]\n",
    "    cm = confusion_matrix(y_test,\n",
    "                          y_pred,\n",
    "                          normalize='true')\n",
    "#\n",
    "    # Отрисовка матрицы ошибок\n",
    "    fig, ax = plt.subplots(figsize=(10, 10))\n",
    "    ax.set_title(f'матрица ошибок', fontsize=18)\n",
    "    disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=self.CLASS_LIST)\n",
    "    disp.plot(ax=ax)\n",
    "    # Стирание ненужной цветовой шкалы\n",
    "    plt.gca().images[-1].colorbar.remove()\n",
    "    plt.xlabel('Предсказанные классы', fontsize=16)\n",
    "    plt.ylabel('Верные классы', fontsize=16)\n",
    "    plt.grid(False)\n",
    "    # Наклон меток горизонтальной оси при необходимости\n",
    "    fig.autofmt_xdate(rotation=45)\n",
    "    plt.show()\n",
    "#\n",
    "  def show_learning_information(self, history, model, augmentation=False):\n",
    "    print('График обучения:')\n",
    "    # Создание полотна для рисунка\n",
    "    plt.figure(1, figsize=(10, 6))\n",
    "#\n",
    "    # Отрисовка графиков 'accuracy' и 'val_accuracy' из значений словаря store_learning.history\n",
    "    plt.plot(history.history['accuracy'],\n",
    "            label='Доля верных ответов на обучающем наборе')\n",
    "    plt.plot(history.history['val_accuracy'],\n",
    "            label='Доля верных ответов на проверочном наборе')\n",
    "    # Задание подписей осей\n",
    "    plt.xlabel('Эпоха обучения')\n",
    "    plt.ylabel('Доля верных ответов')\n",
    "    plt.legend()\n",
    "    # Фиксация графиков и отрисовка всей картинки\n",
    "    plt.show()\n",
    "    print(f'Максимальная точность на обучающей выборке: {round(np.array(history.history[\"accuracy\"]).max(), 3)}')\n",
    "    print(f'Максимальная точность на проверочной выборке: {round(np.array(history.history[\"val_accuracy\"]).max(), 3)}')\n",
    "#\n",
    "    if not augmentation:\n",
    "      print('\\nМатрица ошибок для обучающей выборки:')\n",
    "      self.ShowConfusionMatrix(model, self.sets[0], self.sets[2])\n",
    "      print('\\nМатрица ошибок для проверочной выборки:')\n",
    "      self.ShowConfusionMatrix(model, self.sets[1], self.sets[3])\n",
    "#\n",
    "  def train_model(self, funcModel, count):\n",
    "    # Обучение модели сверточной нейронной сети на подготовленных данных\n",
    "    cnt = count\n",
    "    history_accuracy = []\n",
    "    history_val_accuracy = []\n",
    "    for i in range(cnt):\n",
    "      model = funcModel()\n",
    "      store_learning = model.fit(x_train,\n",
    "                                y_train,\n",
    "                                validation_data=(x_test, y_test),\n",
    "                                shuffle=True,\n",
    "                                batch_size=25,\n",
    "                                epochs=50,\n",
    "                                verbose=0)\n",
    "      history_accuracy.append(store_learning.history['accuracy'])\n",
    "      history_val_accuracy.append(store_learning.history['val_accuracy'])\n",
    "      print(f'Обучение №{i+1} завершено. Результаты')\n",
    "      print(f'Максимальная точность на обучающей выборке: {round(np.array(store_learning.history[\"accuracy\"]).max(), 3)}')\n",
    "      print(f'Максимальная точность на проверочной выборке: {round(np.array(store_learning.history[\"val_accuracy\"]).max(), 3)}')\n",
    "      plt.figure(1, figsize=(10, 6))\n",
    "      plt.plot(store_learning.history['accuracy'],\n",
    "              label='Доля верных ответов на обучающем наборе')\n",
    "      plt.plot(store_learning.history['val_accuracy'],\n",
    "              label='Доля верных ответов на проверочном наборе')\n",
    "      plt.xlabel('Эпоха обучения')\n",
    "      plt.ylabel('Доля верных ответов')\n",
    "      plt.legend()\n",
    "      plt.show()\n",
    "      print('\\n'*3)\n",
    "    print(f'Средняя точность на обучающей выборке: {round(np.array(history_accuracy).max(axis=1).mean(), 3)}')\n",
    "    print(f'Максимальная точность на обучающей выборке: {round(np.array(history_accuracy).max(), 3)}')\n",
    "    print(f'Средняя точность на проверочной выборке: {round(np.array(history_val_accuracy).max(axis=1).mean(), 3)}')\n",
    "    print(f'Максимальная точность на проверочной выборке: {round(np.array(history_val_accuracy).max(), 3)}')\n",
    "#\n",
    "  def train_model_augmentation(self, funcModel, count):\n",
    "    # Обучение модели сверточной нейронной сети на подготовленных данных\n",
    "    cnt = count\n",
    "    history_accuracy = []\n",
    "    history_val_accuracy = []\n",
    "    for i in range(cnt):\n",
    "      model = funcModel()\n",
    "      store_learning = model.fit(self.train_generator,\n",
    "                           validation_data=self.validation_generator,\n",
    "                           batch_size=25,\n",
    "                           epochs=20,\n",
    "                           verbose=1)\n",
    "      history_accuracy.append(store_learning.history['accuracy'])\n",
    "      history_val_accuracy.append(store_learning.history['val_accuracy'])\n",
    "      print(f'Обучение №{i+1} завершено. Результаты')\n",
    "      print(f'Максимальная точность на обучающей выборке: {round(np.array(store_learning.history[\"accuracy\"]).max(), 3)}')\n",
    "      print(f'Максимальная точность на проверочной выборке: {round(np.array(store_learning.history[\"val_accuracy\"]).max(), 3)}')\n",
    "      plt.figure(1, figsize=(10, 6))\n",
    "      plt.plot(store_learning.history['accuracy'],\n",
    "              label='Доля верных ответов на обучающем наборе')\n",
    "      plt.plot(store_learning.history['val_accuracy'],\n",
    "              label='Доля верных ответов на проверочном наборе')\n",
    "      plt.xlabel('Эпоха обучения')\n",
    "      plt.ylabel('Доля верных ответов')\n",
    "      plt.legend()\n",
    "      plt.show()\n",
    "      print('\\n'*3)\n",
    "    print(f'Средняя точность на обучающей выборке: {round(np.array(history_accuracy).max(axis=1).mean(), 3)}')\n",
    "    print(f'Максимальная точность на обучающей выборке: {round(np.array(history_accuracy).max(), 3)}')\n",
    "    print(f'Средняя точность на проверочной выборке: {round(np.array(history_val_accuracy).max(axis=1).mean(), 3)}')\n",
    "    print(f'Максимальная точность на проверочной выборке: {round(np.array(history_val_accuracy).max(), 3)}')\n",
    "worker=Worker()\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b88fe73d",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title <h1><b>---- RUN WORKERS --- <h1><b>\n",
    "# Загрузка датасета\n",
    "worker.load_dataset()\n",
    "#\n",
    "# Показ примеров\n",
    "worker.show_samples()\n",
    "#\n",
    "# Информация о датасете\n",
    "worker.dataset_info()\n",
    "#"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d065bece",
   "metadata": {},
   "source": [
    "@title <h1><b>---- Эксперименты (свои модели) ----</h1><b>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "91d0c701",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ### Эксп. №1 (97.4%, 97.4%)\n",
    "#\n",
    "# Создание выборок\n",
    "IMG_HEIGHT = 64\n",
    "IMG_WIDTH = 64\n",
    "x_train, x_test, y_train, y_test = worker.create_sets((IMG_HEIGHT, IMG_WIDTH))\n",
    "#\n",
    "def create_model():\n",
    "  model = Sequential()\n",
    "  # Первый сверточный блок\n",
    "  model.add(Conv2D(32, (3, 3), padding='same', activation='relu', input_shape=(IMG_HEIGHT, IMG_WIDTH, 1)))\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  # Второй сверточный блок\n",
    "  model.add(Conv2D(32, (3, 3), padding='same', activation='relu'))\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  # Блок классификации\n",
    "  model.add(Flatten())\n",
    "  model.add(Dense(128, activation='relu'))\n",
    "  model.add(Dense(1, activation='sigmoid'))\n",
    "  # Компиляция модели\n",
    "  model.compile(loss='binary_crossentropy', optimizer=Adam(learning_rate=0.0001), metrics=['accuracy'])\n",
    "  return model\n",
    "#\n",
    "model = create_model()\n",
    "#\n",
    "model.summary()\n",
    "#\n",
    "# Обучение модели сверточной нейронной сети на подготовленных данных\n",
    "#\n",
    "store_learning = model.fit(x_train,\n",
    "                           y_train,\n",
    "                           validation_data=(x_test, y_test),\n",
    "                           shuffle=True,\n",
    "                           batch_size=25,\n",
    "                           epochs=30,\n",
    "                           verbose=1)\n",
    "#\n",
    "worker.show_learning_information(store_learning, model, False)\n",
    "#\n",
    "#@title Несколько запусков\n",
    "#\n",
    "worker.train_model(\n",
    "    funcModel=create_model,\n",
    "    count=5)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "91d8fdd4",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ### Эксп. №2 (убран макс-пуллинг) (96.6% 96.6%)\n",
    "#\n",
    "# Создание выборок\n",
    "IMG_HEIGHT = 64\n",
    "IMG_WIDTH = 64\n",
    "x_train, x_test, y_train, y_test = worker.create_sets((IMG_HEIGHT, IMG_WIDTH))\n",
    "#\n",
    "def create_model():\n",
    "  model = Sequential()\n",
    "  # Первый сверточный блок\n",
    "  model.add(Conv2D(32, (3, 3), padding='same', activation='relu', input_shape=(IMG_HEIGHT, IMG_WIDTH, 1)))\n",
    "  #model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  # Второй сверточный блок\n",
    "  model.add(Conv2D(32, (3, 3), padding='same', activation='relu'))\n",
    "  #model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  # Блок классификации\n",
    "  model.add(Flatten())\n",
    "  model.add(Dense(128, activation='relu'))\n",
    "  model.add(Dense(1, activation='sigmoid'))\n",
    "  # Компиляция модели\n",
    "  model.compile(loss='binary_crossentropy', optimizer=Adam(learning_rate=0.0001), metrics=['accuracy'])\n",
    "  return model\n",
    "#\n",
    "model = create_model()\n",
    "#\n",
    "model.summary()\n",
    "#\n",
    "# Обучение модели сверточной нейронной сети на подготовленных данных\n",
    "#\n",
    "store_learning = model.fit(x_train,\n",
    "                           y_train,\n",
    "                           validation_data=(x_test, y_test),\n",
    "                           shuffle=True,\n",
    "                           batch_size=25,\n",
    "                           epochs=30,\n",
    "                           verbose=1)\n",
    "#\n",
    "worker.show_learning_information(store_learning, model)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1de0e5f5",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title Несколько запусков\n",
    "#\n",
    "worker.train_model(\n",
    "    funcModel=create_model,\n",
    "    count=5)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4bac8026",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ### Эксп. №3 (добавлен полносвязный слой в конце) (97.5% 97.6%)\n",
    "#\n",
    "# Создание выборок\n",
    "IMG_HEIGHT = 64\n",
    "IMG_WIDTH = 64\n",
    "x_train, x_test, y_train, y_test = worker.create_sets((IMG_HEIGHT, IMG_WIDTH))\n",
    "#\n",
    "def create_model():\n",
    "  model = Sequential()\n",
    "  # Первый сверточный блок\n",
    "  model.add(Conv2D(32, (3, 3), padding='same', activation='relu', input_shape=(IMG_HEIGHT, IMG_WIDTH, 1)))\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  # Второй сверточный блок\n",
    "  model.add(Conv2D(32, (3, 3), padding='same', activation='relu'))\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  # Блок классификации\n",
    "  model.add(Flatten())\n",
    "  model.add(Dense(128, activation='relu'))\n",
    "  model.add(Dense(128, activation='relu'))\n",
    "  model.add(Dense(1, activation='sigmoid'))\n",
    "  # Компиляция модели\n",
    "  model.compile(loss='binary_crossentropy', optimizer=Adam(learning_rate=0.0001), metrics=['accuracy'])\n",
    "  return model\n",
    "#\n",
    "model = create_model()\n",
    "#\n",
    "model.summary()\n",
    "#\n",
    "# Обучение модели сверточной нейронной сети на подготовленных данных\n",
    "#\n",
    "store_learning = model.fit(x_train,\n",
    "                           y_train,\n",
    "                           validation_data=(x_test, y_test),\n",
    "                           shuffle=True,\n",
    "                           batch_size=25,\n",
    "                           epochs=30,\n",
    "                           verbose=1)\n",
    "#\n",
    "worker.show_learning_information(store_learning, model)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5e78c842",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title Несколько запусков\n",
    "#\n",
    "worker.train_model(\n",
    "    funcModel=create_model,\n",
    "    count=5)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e045afdc",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ### Эксп. №4 (усилен полносвязный слой в конце) (97.5% 97.6%)\n",
    "#\n",
    "# Создание выборок\n",
    "IMG_HEIGHT = 64\n",
    "IMG_WIDTH = 64\n",
    "x_train, x_test, y_train, y_test = worker.create_sets((IMG_HEIGHT, IMG_WIDTH))\n",
    "#\n",
    "def create_model():\n",
    "  model = Sequential()\n",
    "  # Первый сверточный блок\n",
    "  # Первый сверточный блок\n",
    "  model.add(Conv2D(32, (3, 3), padding='same', activation='relu', input_shape=(IMG_HEIGHT, IMG_WIDTH, 1)))\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  # Второй сверточный блок\n",
    "  model.add(Conv2D(32, (3, 3), padding='same', activation='relu'))\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  # Блок классификации\n",
    "  model.add(Flatten())\n",
    "  model.add(Dense(256, activation='relu'))\n",
    "  model.add(Dense(128, activation='relu'))\n",
    "  model.add(Dense(1, activation='sigmoid'))\n",
    "  # Компиляция модели\n",
    "  model.compile(loss='binary_crossentropy', optimizer=Adam(learning_rate=0.0001), metrics=['accuracy'])\n",
    "  return model\n",
    "#\n",
    "model = create_model()\n",
    "#\n",
    "model.summary()\n",
    "#\n",
    "# Обучение модели сверточной нейронной сети на подготовленных данных\n",
    "#\n",
    "store_learning = model.fit(x_train,\n",
    "                           y_train,\n",
    "                           validation_data=(x_test, y_test),\n",
    "                           shuffle=True,\n",
    "                           batch_size=25,\n",
    "                           epochs=30,\n",
    "                           verbose=1)\n",
    "#\n",
    "worker.show_learning_information(store_learning, model)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "192c59b5",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title Несколько запусков\n",
    "#\n",
    "worker.train_model(\n",
    "    funcModel=create_model,\n",
    "    count=5)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7fab8364",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ### Эксп. №5 (ослаблен полносвязный в конце) (97.5% 97.7%)\n",
    "#\n",
    "# Создание выборок\n",
    "IMG_HEIGHT = 64\n",
    "IMG_WIDTH = 64\n",
    "x_train, x_test, y_train, y_test = worker.create_sets((IMG_HEIGHT, IMG_WIDTH))\n",
    "#\n",
    "def create_model():\n",
    "  model = Sequential()\n",
    "  # Первый сверточный блок\n",
    "  model.add(Conv2D(32, (3, 3), padding='same', activation='relu', input_shape=(IMG_HEIGHT, IMG_WIDTH, 1)))\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  # Второй сверточный блок\n",
    "  model.add(Conv2D(32, (3, 3), padding='same', activation='relu'))\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  # Блок классификации\n",
    "  model.add(Flatten())\n",
    "  model.add(Dense(64, activation='relu'))\n",
    "  model.add(Dense(128, activation='relu'))\n",
    "  model.add(Dense(1, activation='sigmoid'))\n",
    "  # Компиляция модели\n",
    "  model.compile(loss='binary_crossentropy', optimizer=Adam(learning_rate=0.0001), metrics=['accuracy'])\n",
    "  return model\n",
    "#\n",
    "model = create_model()\n",
    "#\n",
    "model.summary()\n",
    "#\n",
    "# Обучение модели сверточной нейронной сети на подготовленных данных\n",
    "#\n",
    "store_learning = model.fit(x_train,\n",
    "                           y_train,\n",
    "                           validation_data=(x_test, y_test),\n",
    "                           shuffle=True,\n",
    "                           batch_size=25,\n",
    "                           epochs=30,\n",
    "                           verbose=1)\n",
    "#\n",
    "worker.show_learning_information(store_learning, model)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4a669687",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title Несколько запусков\n",
    "#\n",
    "worker.train_model(\n",
    "    funcModel=create_model,\n",
    "    count=5)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6dadf929",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ### Эксп. №6 (ослаблен первый сверточный) (97.4% 97.7%)\n",
    "#\n",
    "# Создание выборок\n",
    "IMG_HEIGHT = 64\n",
    "IMG_WIDTH = 64\n",
    "x_train, x_test, y_train, y_test = worker.create_sets((IMG_HEIGHT, IMG_WIDTH))\n",
    "#\n",
    "def create_model():\n",
    "  model = Sequential()\n",
    "  # Первый сверточный блок\n",
    "  model.add(Conv2D(12, (3, 3), padding='same', activation='relu', input_shape=(IMG_HEIGHT, IMG_WIDTH, 1)))\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  # Второй сверточный блок\n",
    "  model.add(Conv2D(32, (3, 3), padding='same', activation='relu'))\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  # Блок классификации\n",
    "  model.add(Flatten())\n",
    "  model.add(Dense(128, activation='relu'))\n",
    "  model.add(Dense(128, activation='relu'))\n",
    "  model.add(Dense(1, activation='sigmoid'))\n",
    "  # Компиляция модели\n",
    "  model.compile(loss='binary_crossentropy', optimizer=Adam(learning_rate=0.0001), metrics=['accuracy'])\n",
    "  return model\n",
    "#\n",
    "model = create_model()\n",
    "#\n",
    "model.summary()\n",
    "#\n",
    "# Обучение модели сверточной нейронной сети на подготовленных данных\n",
    "#\n",
    "store_learning = model.fit(x_train,\n",
    "                           y_train,\n",
    "                           validation_data=(x_test, y_test),\n",
    "                           shuffle=True,\n",
    "                           batch_size=25,\n",
    "                           epochs=30,\n",
    "                           verbose=1)\n",
    "#\n",
    "worker.show_learning_information(store_learning, model)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1b7ca027",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title Несколько запусков\n",
    "#\n",
    "worker.train_model(\n",
    "    funcModel=create_model,\n",
    "    count=5)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b3576216",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ### Эксп. №7 (усилен первый сверточный) (97.4% 97.7%)\n",
    "#\n",
    "# Создание выборок\n",
    "IMG_HEIGHT = 64\n",
    "IMG_WIDTH = 64\n",
    "x_train, x_test, y_train, y_test = worker.create_sets((IMG_HEIGHT, IMG_WIDTH))\n",
    "#\n",
    "def create_model():\n",
    "  model = Sequential()\n",
    "  # Первый сверточный блок\n",
    "  model.add(Conv2D(128, (3, 3), padding='same', activation='relu', input_shape=(IMG_HEIGHT, IMG_WIDTH, 1)))\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  # Второй сверточный блок\n",
    "  model.add(Conv2D(32, (3, 3), padding='same', activation='relu'))\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  # Блок классификации\n",
    "  model.add(Flatten())\n",
    "  model.add(Dense(128, activation='relu'))\n",
    "  model.add(Dense(128, activation='relu'))\n",
    "  model.add(Dense(1, activation='sigmoid'))\n",
    "  # Компиляция модели\n",
    "  model.compile(loss='binary_crossentropy', optimizer=Adam(learning_rate=0.0001), metrics=['accuracy'])\n",
    "  return model\n",
    "#\n",
    "model = create_model()\n",
    "#\n",
    "model.summary()\n",
    "#\n",
    "# Обучение модели сверточной нейронной сети на подготовленных данных\n",
    "#\n",
    "store_learning = model.fit(x_train,\n",
    "                           y_train,\n",
    "                           validation_data=(x_test, y_test),\n",
    "                           shuffle=True,\n",
    "                           batch_size=25,\n",
    "                           epochs=30,\n",
    "                           verbose=1)\n",
    "#\n",
    "worker.show_learning_information(store_learning, model)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "90295022",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title Несколько запусков\n",
    "#\n",
    "worker.train_model(\n",
    "    funcModel=create_model,\n",
    "    count=5)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e61fc4c6",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ### Эксп. №8 (усилен второй сверточный) (97.8% 98.1%)\n",
    "#\n",
    "# Создание выборок\n",
    "IMG_HEIGHT = 64\n",
    "IMG_WIDTH = 64\n",
    "x_train, x_test, y_train, y_test = worker.create_sets((IMG_HEIGHT, IMG_WIDTH))\n",
    "#\n",
    "def create_model():\n",
    "  model = Sequential()\n",
    "  # Первый сверточный блок\n",
    "  model.add(Conv2D(128, (3, 3), padding='same', activation='relu', input_shape=(IMG_HEIGHT, IMG_WIDTH, 1)))\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  # Второй сверточный блок\n",
    "  model.add(Conv2D(256, (3, 3), padding='same', activation='relu'))\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  # Блок классификации\n",
    "  model.add(Flatten())\n",
    "  model.add(Dense(128, activation='relu'))\n",
    "  model.add(Dense(128, activation='relu'))\n",
    "  model.add(Dense(1, activation='sigmoid'))\n",
    "  # Компиляция модели\n",
    "  model.compile(loss='binary_crossentropy', optimizer=Adam(learning_rate=0.0001), metrics=['accuracy'])\n",
    "  return model\n",
    "#\n",
    "model = create_model()\n",
    "#\n",
    "model.summary()\n",
    "#\n",
    "# Обучение модели сверточной нейронной сети на подготовленных данных\n",
    "#\n",
    "store_learning = model.fit(x_train,\n",
    "                           y_train,\n",
    "                           validation_data=(x_test, y_test),\n",
    "                           shuffle=True,\n",
    "                           batch_size=25,\n",
    "                           epochs=30,\n",
    "                           verbose=1)\n",
    "#\n",
    "worker.show_learning_information(store_learning, model)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e021fab9",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title Несколько запусков\n",
    "#\n",
    "worker.train_model(\n",
    "    funcModel=create_model,\n",
    "    count=5)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bde90df4",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ### Эксп. №9 (добавлен третий сверточный) (98.1% 98.4%)\n",
    "#\n",
    "# Создание выборок\n",
    "IMG_HEIGHT = 64\n",
    "IMG_WIDTH = 64\n",
    "x_train, x_test, y_train, y_test = worker.create_sets((IMG_HEIGHT, IMG_WIDTH))\n",
    "#\n",
    "def create_model():\n",
    "  model = Sequential()\n",
    "  model.add(Conv2D(128, (3, 3), padding='same', activation='relu', input_shape=(IMG_HEIGHT, IMG_WIDTH, 1)))\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  # Второй сверточный блок\n",
    "  model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  # Третий сверточный блок\n",
    "  model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  # Блок классификации\n",
    "  model.add(Flatten())\n",
    "  model.add(Dense(128, activation='relu'))\n",
    "  model.add(Dense(128, activation='relu'))\n",
    "  model.add(Dense(1, activation='sigmoid'))\n",
    "  # Компиляция модели\n",
    "  model.compile(loss='binary_crossentropy', optimizer=Adam(learning_rate=0.0001), metrics=['accuracy'])\n",
    "  return model\n",
    "#\n",
    "model = create_model()\n",
    "#\n",
    "model.summary()\n",
    "#\n",
    "# Обучение модели сверточной нейронной сети на подготовленных данных\n",
    "#\n",
    "store_learning = model.fit(x_train,\n",
    "                           y_train,\n",
    "                           validation_data=(x_test, y_test),\n",
    "                           shuffle=True,\n",
    "                           batch_size=25,\n",
    "                           epochs=30,\n",
    "                           verbose=1)\n",
    "#\n",
    "worker.show_learning_information(store_learning, model)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f1b293ce",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title Несколько запусков\n",
    "#\n",
    "worker.train_model(\n",
    "    funcModel=create_model,\n",
    "    count=5)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "193d7f76",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ### Эксп. №10 (добавлен четвертый сверточный) (98.3% 98.6%)\n",
    "#\n",
    "# Создание выборок\n",
    "IMG_HEIGHT = 64\n",
    "IMG_WIDTH = 64\n",
    "x_train, x_test, y_train, y_test = worker.create_sets((IMG_HEIGHT, IMG_WIDTH))\n",
    "#\n",
    "def create_model():\n",
    "  model = Sequential()\n",
    "  model.add(Conv2D(128, (3, 3), padding='same', activation='relu', input_shape=(IMG_HEIGHT, IMG_WIDTH, 1)))\n",
    "  model.add(MaxPooling2D(pool_size=(2, 2)))\n",
    "#\n",
    "  # Второй сверточный блок\n",
    "  model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))\n",
    "  model.add(MaxPooling2D(pool_size=(2, 2)))\n",
    "#\n",
    "  # Третий сверточный блок\n",
    "  model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))\n",
    "  model.add(MaxPooling2D(pool_size=(2, 2)))\n",
    "#\n",
    "  # Четвертый сверточный блок\n",
    "  model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))\n",
    "  model.add(MaxPooling2D(pool_size=(2, 2)))\n",
    "#\n",
    "  # Блок классификации\n",
    "  model.add(Flatten())\n",
    "  model.add(Dense(128, activation='relu'))\n",
    "  model.add(Dense(128, activation='relu'))\n",
    "  model.add(Dense(1, activation='sigmoid'))\n",
    "  # Компиляция модели\n",
    "  model.compile(loss='binary_crossentropy', optimizer=Adam(learning_rate=0.0001), metrics=['accuracy'])\n",
    "  return model\n",
    "#\n",
    "model = create_model()\n",
    "#\n",
    "model.summary()\n",
    "#\n",
    "# Обучение модели сверточной нейронной сети на подготовленных данных\n",
    "#\n",
    "store_learning = model.fit(x_train,\n",
    "                           y_train,\n",
    "                           validation_data=(x_test, y_test),\n",
    "                           shuffle=True,\n",
    "                           batch_size=25,\n",
    "                           epochs=30,\n",
    "                           verbose=1)\n",
    "#\n",
    "worker.show_learning_information(store_learning, model)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a2384c12",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title Несколько запусков\n",
    "#\n",
    "worker.train_model(\n",
    "    funcModel=create_model,\n",
    "    count=5)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "224e14bd",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ### Эксп. №11 (добавлен BatchNorm + Dropout) (98.7% 98.9%)\n",
    "#\n",
    "# Создание выборок\n",
    "IMG_HEIGHT = 64\n",
    "IMG_WIDTH = 64\n",
    "x_train, x_test, y_train, y_test = worker.create_sets((IMG_HEIGHT, IMG_WIDTH))\n",
    "#\n",
    "def create_model():\n",
    "  model = Sequential()\n",
    "  model.add(Conv2D(128, (3, 3), padding='same', activation='relu', input_shape=(IMG_HEIGHT, IMG_WIDTH, 1)))\n",
    "  model.add(BatchNormalization())\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  # Второй сверточный блок\n",
    "  model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))\n",
    "  model.add(BatchNormalization())\n",
    "#\n",
    "  model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "  model.add(Dropout(0.2))\n",
    "#\n",
    "  # Блок классификации\n",
    "  model.add(Flatten())\n",
    "  model.add(Dense(128, activation='relu'))\n",
    "  model.add(Dense(128, activation='relu'))\n",
    "  model.add(Dense(1, activation='sigmoid'))\n",
    "  # Компиляция модели\n",
    "  model.compile(loss='binary_crossentropy', optimizer=Adam(learning_rate=0.0001), metrics=['accuracy'])\n",
    "  return model\n",
    "#\n",
    "model = create_model()\n",
    "#\n",
    "model.summary()\n",
    "#\n",
    "# Обучение модели сверточной нейронной сети на подготовленных данных\n",
    "#\n",
    "store_learning = model.fit(x_train,\n",
    "                           y_train,\n",
    "                           validation_data=(x_test, y_test),\n",
    "                           shuffle=True,\n",
    "                           batch_size=25,\n",
    "                           epochs=30,\n",
    "                           verbose=1)\n",
    "#\n",
    "worker.show_learning_information(store_learning, model)\n",
    "#\n",
    "#@title Несколько запусков\n",
    "#\n",
    "worker.train_model(\n",
    "    funcModel=create_model,\n",
    "    count=5)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "72365ab9",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ### Эксп. №12 (Переход на 128х128) (98.9% 98.9%)\n",
    "#\n",
    "# Создание выборок\n",
    "IMG_HEIGHT = 128\n",
    "IMG_WIDTH = 128\n",
    "x_train, x_test, y_train, y_test = worker.create_sets((IMG_HEIGHT, IMG_WIDTH))\n",
    "#\n",
    "def create_model():\n",
    "  model = Sequential()\n",
    "  model.add(Conv2D(128, (3, 3), padding='same', activation='relu', input_shape=(IMG_HEIGHT, IMG_WIDTH, 1)))\n",
    "  model.add(BatchNormalization())\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  # Второй сверточный блок\n",
    "  model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))\n",
    "  model.add(BatchNormalization())\n",
    "#\n",
    "  model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "  model.add(Dropout(0.2))\n",
    "#\n",
    "  # Блок классификации\n",
    "  model.add(Flatten())\n",
    "  model.add(Dense(128, activation='relu'))\n",
    "  model.add(Dense(128, activation='relu'))\n",
    "  model.add(Dense(1, activation='sigmoid'))\n",
    "  # Компиляция модели\n",
    "  model.compile(loss='binary_crossentropy', optimizer=Adam(learning_rate=0.0001), metrics=['accuracy'])\n",
    "  return model\n",
    "#\n",
    "model = create_model()\n",
    "#\n",
    "model.summary()\n",
    "#\n",
    "# Обучение модели сверточной нейронной сети на подготовленных данных\n",
    "#\n",
    "store_learning = model.fit(x_train,\n",
    "                           y_train,\n",
    "                           validation_data=(x_test, y_test),\n",
    "                           shuffle=True,\n",
    "                           batch_size=25,\n",
    "                           epochs=30,\n",
    "                           verbose=1)\n",
    "#\n",
    "worker.show_learning_information(store_learning, model)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "09843634",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title Несколько запусков\n",
    "#\n",
    "worker.train_model(\n",
    "    funcModel=create_model,\n",
    "    count=5)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a055a1ba",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ### Эксп. №13 (Смена ядра сверточного слоя) (98.9% 98.9%)\n",
    "#\n",
    "# Создание выборок\n",
    "IMG_HEIGHT = 128\n",
    "IMG_WIDTH = 128\n",
    "x_train, x_test, y_train, y_test = worker.create_sets((IMG_HEIGHT, IMG_WIDTH))\n",
    "#\n",
    "def create_model():\n",
    "  model = Sequential()\n",
    "  model.add(Conv2D(128, (2, 2), padding='same', activation='relu', input_shape=(IMG_HEIGHT, IMG_WIDTH, 1)))\n",
    "  model.add(BatchNormalization())\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))\n",
    "  model.add(BatchNormalization())\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))\n",
    "  model.add(BatchNormalization())\n",
    "#\n",
    "  model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))\n",
    "  model.add(BatchNormalization())\n",
    "#\n",
    "  model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))\n",
    "  model.add(BatchNormalization())\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  model.add(Dropout(0.2))\n",
    "#\n",
    "#\n",
    "  # Блок классификации\n",
    "  model.add(Flatten())\n",
    "  model.add(Dense(128, activation='relu'))\n",
    "  model.add(Dense(128, activation='relu'))\n",
    "  model.add(Dense(1, activation='sigmoid'))\n",
    "  # Компиляция модели\n",
    "  model.compile(loss='binary_crossentropy', optimizer=Adam(learning_rate=0.0001), metrics=['accuracy'])\n",
    "  return model\n",
    "#\n",
    "model = create_model()\n",
    "#\n",
    "model.summary()\n",
    "#\n",
    "# Обучение модели сверточной нейронной сети на подготовленных данных\n",
    "#\n",
    "store_learning = model.fit(x_train,\n",
    "                           y_train,\n",
    "                           validation_data=(x_test, y_test),\n",
    "                           shuffle=True,\n",
    "                           batch_size=25,\n",
    "                           epochs=30,\n",
    "                           verbose=1)\n",
    "#\n",
    "worker.show_learning_information(store_learning, model)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5c3a84fc",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title Несколько запусков\n",
    "#\n",
    "worker.train_model(\n",
    "    funcModel=create_model,\n",
    "    count=5)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ce8e86d5",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ### Эксп. №14 (Смена ядра 2 и последующих сверточных слоев) (98.8% 99.1%)\n",
    "#\n",
    "# Создание выборок\n",
    "IMG_HEIGHT = 128\n",
    "IMG_WIDTH = 128\n",
    "x_train, x_test, y_train, y_test = worker.create_sets((IMG_HEIGHT, IMG_WIDTH))\n",
    "#\n",
    "def create_model():\n",
    "  model = Sequential()\n",
    "#\n",
    "  model.add(Conv2D(128, (2, 2), padding='same', activation='relu', input_shape=(IMG_HEIGHT, IMG_WIDTH, 1)))\n",
    "  model.add(BatchNormalization())\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  model.add(Conv2D(128, (5, 5), padding='same', activation='relu'))\n",
    "  model.add(BatchNormalization())\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  model.add(Conv2D(128, (5, 5), padding='same', activation='relu'))\n",
    "  model.add(BatchNormalization())\n",
    "#\n",
    "  model.add(Conv2D(128, (5, 5), padding='same', activation='relu'))\n",
    "  model.add(BatchNormalization())\n",
    "#\n",
    "  model.add(Conv2D(128, (5, 5), padding='same', activation='relu'))\n",
    "  model.add(BatchNormalization())\n",
    "  model.add(MaxPooling2D(pool_size=(3, 3)))\n",
    "#\n",
    "  model.add(Dropout(0.2))\n",
    "#\n",
    "  # Блок классификации\n",
    "  model.add(Flatten())\n",
    "  model.add(Dense(128, activation='relu'))\n",
    "  model.add(Dense(128, activation='relu'))\n",
    "  model.add(Dense(1, activation='sigmoid'))\n",
    "  # Компиляция модели\n",
    "  model.compile(loss='binary_crossentropy', optimizer=Adam(learning_rate=0.0001), metrics=['accuracy'])\n",
    "  return model\n",
    "#\n",
    "model = create_model()\n",
    "#\n",
    "model.summary()\n",
    "#\n",
    "# Обучение модели сверточной нейронной сети на подготовленных данных\n",
    "#\n",
    "store_learning = model.fit(x_train,\n",
    "                           y_train,\n",
    "                           validation_data=(x_test, y_test),\n",
    "                           shuffle=True,\n",
    "                           batch_size=25,\n",
    "                           epochs=30,\n",
    "                           verbose=1)\n",
    "#\n",
    "worker.show_learning_information(store_learning, model)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3f9b8f79",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title Несколько запусков\n",
    "#\n",
    "worker.train_model(\n",
    "    funcModel=create_model,\n",
    "    count=5)\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b40a6307",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title <h1><b> Эксперименты (предобученные модели)</b></h1>\n",
    "#\n",
    "# Загрузка датасета\n",
    "worker_pretrain.load_dataset()\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "82c02e92",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ### Эксп. №1 NASNetMobile (99.3%)\n",
    "#\n",
    "model = worker_pretrain.createAndTrainNASNetMobile()\n",
    "#\n",
    "# Схема сети\n",
    "plot_model(model.layers[4])\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "da71fc2b",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ### Эксп. №2 ResNet50 (99.04%)\n",
    "#\n",
    "model = worker_pretrain.createAndTrainResNet()\n",
    "#\n",
    "# Схема сети\n",
    "plot_model(model.layers[4])\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "af9bcf2b",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ### Эксп. №3 ResNet50v2 (99.1%)\n",
    "#\n",
    "model = worker_pretrain.createAndTrainResNet50V2()\n",
    "#\n",
    "# Схема сети\n",
    "plot_model(model.layers[4])\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a62b8a8b",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ### Эксп. №4 VGG16 (98.9%)\n",
    "#\n",
    "model = worker_pretrain.createAndTrainVGG16()\n",
    "#\n",
    "# Схема сети\n",
    "plot_model(model.layers[4])\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "944553dd",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ### Эксп. №5 VGG19  (98.8%)\n",
    "#\n",
    "model = worker_pretrain.createAndTrainVGG19()\n",
    "#\n",
    "# Схема сети\n",
    "plot_model(model.layers[4])\n",
    "#"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4ab22145",
   "metadata": {},
   "outputs": [],
   "source": [
    "#@title ### Эксп. №6 XCeption (99.4%)\n",
    "#\n",
    "model = worker_pretrain.createAndTrainXception()\n",
    "#\n",
    "# Схема сети\n",
    "plot_model(model.layers[4])\n",
    "#"
   ]
  }
 ],
 "metadata": {
  "jupytext": {
   "cell_metadata_filter": "-all",
   "encoding": "# -*- coding: utf-8 -*-",
   "formats": "ipynb,py",
   "main_language": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
